﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using ModeloGaleriaNET;

namespace SeguridadGaleria
{
    public class GeneradorTipos
    {
        private GeneradorTipos()
        { }

        public static readonly GeneradorTipos Instance = new GeneradorTipos();

        public Type GenerarTipoPermisoGrupos(List<GrupoValue> grupos)
        {
            // create a dynamic assembly and module 
            AssemblyName assemblyName = new AssemblyName();
            assemblyName.Name = "tmpAssembly";
            AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder module = assemblyBuilder.DefineDynamicModule("tmpModule");

            // create a new type builder
            TypeBuilder typeBuilder = module.DefineType("TipoPermisoGrupos", TypeAttributes.Public | TypeAttributes.Class);

            GenerateProperty(typeBuilder, "Permisos", typeof(string));

            foreach (GrupoValue g in grupos)
                GenerateProperty(typeBuilder, g.Nombre, typeof(bool));

            return typeBuilder.CreateType();
        }

        private void GenerateProperty(TypeBuilder typeBuilder, string propertyName, Type tipo)
        {
            FieldBuilder field = typeBuilder.DefineField("_" + propertyName, tipo, FieldAttributes.Private);
            // Generate a public property
            PropertyBuilder property =
                typeBuilder.DefineProperty(propertyName,
                                 PropertyAttributes.None,
                                 tipo,
                                 new Type[] { tipo });

            MethodAttributes GetSetAttr =
                    MethodAttributes.Public |
                    MethodAttributes.HideBySig;

            // Define the "get" accessor method for current private field.
            MethodBuilder currGetPropMthdBldr =
                typeBuilder.DefineMethod(propertyName,
                                           GetSetAttr,
                                           tipo,
                                           Type.EmptyTypes);

            // Intermediate Language stuff...
            ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
            currGetIL.Emit(OpCodes.Ldarg_0);
            currGetIL.Emit(OpCodes.Ldfld, field);
            currGetIL.Emit(OpCodes.Ret);

            MethodBuilder currSetPropMthdBldr =
                    typeBuilder.DefineMethod(propertyName,
                                               GetSetAttr,
                                               null,
                                               new Type[] { tipo });

            // Again some Intermediate Language stuff...
            ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
            currSetIL.Emit(OpCodes.Ldarg_0);
            currSetIL.Emit(OpCodes.Ldarg_1);
            currSetIL.Emit(OpCodes.Stfld, field);
            currSetIL.Emit(OpCodes.Ret);

            // Last, we must map the two methods created above to our PropertyBuilder to 
            // their corresponding behaviors, "get" and "set" respectively. 
            property.SetGetMethod(currGetPropMthdBldr);
            property.SetSetMethod(currSetPropMthdBldr);
        }
    }
}
